/* -----------------------------------------------------------------------------
*  CFileTxtIn
*  Author: Antonio Javier Gallego Sánchez (ajgallego AT gmail DOT com)
*  License: GNU Public License
* ----------------------------------------------------------------------------*/

#include "CFileTxtIn.hpp"

#include <iostream>
#include <fstream>
#include <sstream>
#include <string.h>
#include <algorithm>

#include "CVectorString.hpp"
#include "asrtbas.h"
using namespace std;

struct prv_fileIn_t
{
    ifstream file;
};

//------------------------------------------------------------------------------
//
static struct prv_fileIn_t* prv_create()
{
    prv_fileIn_t *data = new prv_fileIn_t;
    assert_no_null( data );

    return data;
}

//------------------------------------------------------------------------------
//
CFileTxtIn :: CFileTxtIn( const char* filename )
{
    m_data = prv_create();

    m_data->file.open( filename, ios::in );

    assert( m_data->file.fail() == false &&
            "There was an error opening the file" );
}

//------------------------------------------------------------------------------
//
CFileTxtIn :: ~CFileTxtIn()
{
    m_data->file.close();

    delete m_data;
}

//------------------------------------------------------------------------------
//
bool CFileTxtIn :: eof () const
{
    if( m_data->file.eof() == true )
        return true;

    m_data->file.get();

    if( m_data->file.gcount() == 1 )
    {
        m_data->file.clear();
        m_data->file.unget();

        return false;
    }

    m_data->file.clear( ios_base::eofbit );

    return true;
}

//------------------------------------------------------------------------------
//
static unsigned int prv_getCurrentPos( ifstream *file )
{
    assert_no_null( file );

    file->clear();

    const std::streampos currentPos = file->tellg();
    assert( currentPos >= 0 );

    return currentPos;
}

//------------------------------------------------------------------------------
//
static void prv_setFileToBeginning( ifstream *file )
{
    assert_no_null( file );

    file->clear();

    file->seekg( 0, ios::beg );
}

//------------------------------------------------------------------------------
//
static void prv_setFileToEnd( ifstream *file )
{
    assert_no_null( file );

    file->clear();

    file->seekg( 0, ios::end );

    file->clear( ios_base::eofbit );
}

//------------------------------------------------------------------------------
//
static void prv_setFileToPosition( int position, ifstream *file )
{
    assert_no_null( file );

    file->clear();

    file->seekg( position );
}

//------------------------------------------------------------------------------
//
static unsigned int prv_countLines( ifstream *file )
{
    assert_no_null( file );

    return count( istreambuf_iterator<char>( *file ),
                  istreambuf_iterator<char>(),'\n');
}

//------------------------------------------------------------------------------
//
bool CFileTxtIn :: ungetChar()
{
    m_data->file.clear();

    m_data->file.unget();

    if( m_data->file.fail() )
        return false;

    return true;
}

//------------------------------------------------------------------------------
//
void CFileTxtIn :: ungetAll()
{
    prv_setFileToBeginning( &m_data->file );
}

//------------------------------------------------------------------------------
//
unsigned int CFileTxtIn :: totalLines() const
{
    unsigned int currentPos = prv_getCurrentPos( &m_data->file );

    prv_setFileToBeginning( &m_data->file );

    unsigned int numLines = prv_countLines( &m_data->file );

    prv_setFileToPosition( currentPos, &m_data->file );

    return numLines + 1;
}

//------------------------------------------------------------------------------
//
unsigned int CFileTxtIn :: remainingLines() const
{
    if( this->eof() == true )
        return 0;

    unsigned int currentPos = prv_getCurrentPos( &m_data->file );

    unsigned int numLines = prv_countLines( &m_data->file );

    prv_setFileToPosition( currentPos, &m_data->file );

    return numLines + 1;
}

//------------------------------------------------------------------------------
//
unsigned int CFileTxtIn :: totalBytes() const
{
    unsigned int currentPos = prv_getCurrentPos( &m_data->file );

    prv_setFileToBeginning( &m_data->file );
    unsigned int beginningPos = prv_getCurrentPos( &m_data->file );

    prv_setFileToEnd( &m_data->file );
    unsigned int endPos = prv_getCurrentPos( &m_data->file );

    unsigned int totalBytes = endPos - beginningPos;

    prv_setFileToPosition( currentPos, &m_data->file );

    return totalBytes;
}

//------------------------------------------------------------------------------
//
unsigned int CFileTxtIn :: remainingBytes() const
{
    if( this->eof() == true )
        return 0;

    unsigned int currentPos = prv_getCurrentPos( &m_data->file );

    prv_setFileToEnd( &m_data->file );
    unsigned int endPos = prv_getCurrentPos( &m_data->file );

    unsigned remainingBytes = endPos - currentPos;

    prv_setFileToPosition( currentPos, &m_data->file );

    return remainingBytes;
}

//------------------------------------------------------------------------------
//
char CFileTxtIn :: getChar()
{
    assert( m_data->file.eof() == false );

    char c = m_data->file.get();

    assert( m_data->file.gcount() == 1 );

    return c;
}

//------------------------------------------------------------------------------
//
const char* CFileTxtIn :: getString()
{
    assert( m_data->file.eof() == false );

    string str;

    getline( m_data->file, str );

    assert( m_data->file.gcount() >= 1 );

    return strdup( str.c_str() );
}

//------------------------------------------------------------------------------
//
bool CFileTxtIn :: getBool()
{
    char c = this->getChar();

    assert( c == '0' || c == '1' );

    if( c == '0' )
        return false;

    return true;
}

//------------------------------------------------------------------------------
//
int CFileTxtIn :: getInt()
{
    assert( m_data->file.eof() == false );

    int value;

    m_data->file >> value;

    assert( m_data->file.fail() == false );

    return value;
}

//------------------------------------------------------------------------------
//
unsigned int CFileTxtIn :: getUInt()
{
    assert( m_data->file.eof() == false );

    unsigned int value;

    m_data->file >> value;

    assert( m_data->file.fail() == false );

    return value;
}

//------------------------------------------------------------------------------
//
double CFileTxtIn :: getDouble()
{
    assert( m_data->file.eof() == false );

    double value;

    m_data->file >> value;

    assert( m_data->file.fail() == false );

    return value;
}

//------------------------------------------------------------------------------
//
class CVectorString* CFileTxtIn :: getVectorWithAllFileContent() const
{
    unsigned int currentPos = prv_getCurrentPos( &m_data->file );

    prv_setFileToBeginning( &m_data->file );

    CVectorString *content = new CVectorString();

    string line;

    while( this->eof() == false )
    {
        getline( m_data->file, line );

        content->push_back( strdup( line.c_str() ) );
    }

    prv_setFileToPosition( currentPos, &m_data->file );

    return content;
}

//------------------------------------------------------------------------------
//
const char* CFileTxtIn :: getStringWithAllFileContent( const char* separator ) const
{
    unsigned int currentPos = prv_getCurrentPos( &m_data->file );

    prv_setFileToBeginning( &m_data->file );

    string line;
    string content = "";

    while( this->eof() == false )
    {
        getline( m_data->file, line );

        content.append( line + string( separator ) );
    }

    prv_setFileToPosition( currentPos, &m_data->file );

    return strdup( content.c_str() );
}

//------------------------------------------------------------------------------
//
void CFileTxtIn :: print() const
{
    const char* content = this->getStringWithAllFileContent( "\n" );

    cout << content;
}

/**
*/